package com.imooc.business.builder;

import com.imooc.business.domain.Collect;
import com.imooc.business.domain.Metadata;
import com.imooc.business.domain.Rule;
import com.imooc.business.domain.Storage;
import com.imooc.infra.repository.jdbc.params.StandardSqlParam;
import com.imooc.module.collect.enums.DimensionEnum;
import com.imooc.module.collect.enums.PeriodEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.assertj.core.util.Lists;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author Tim
 * @Description:
 * @Version 1.0
 */
public class CollectSqlBuilder {
    /**
     * 构建高维度Sql采集
     * @param rule
     * @param dimension
     * @param period
     * @return
     */
    public static List<String> buildSql(Rule rule, DimensionEnum dimension, PeriodEnum period) {
        List<String> sqlList = Lists.newArrayList();
        //周期维度相关， 空间维度相关
        String periodAndDimensionSql = buildPeriodAndDimension(rule, dimension, period);
        String periodAndNotDimensionSql = buildPeriodAndNotDimension(rule, dimension, period);
        String notPeriodAndDimensionSql = buildNotPeriodAndDimension(rule, dimension, period);
        String notPeriodAndNotDimensionSql = buildNotPeriodAndNotDimension(rule, dimension, period);
        sqlList.add(periodAndDimensionSql);
        sqlList.add(periodAndNotDimensionSql);
        sqlList.add(notPeriodAndDimensionSql);
        sqlList.add(notPeriodAndNotDimensionSql);
        return sqlList.stream().filter(Objects::nonNull).collect(Collectors.toList());
    }

    private static String buildNotPeriodAndNotDimension(Rule rule, DimensionEnum dimension, PeriodEnum period) {
        List<Collect> collects = rule.getPredicateCollect(Metadata::notPeriodAndNotDimension);
        if (CollectionUtils.isEmpty(collects)) {
            return null;
        }
        //获取统计的列  a,b,c 。 sum(a) as a, sum(b) as b, sum(c) as c,
        String queryColumn = collects.stream().map(Collect::getEnName).distinct().
                map(enName -> "sum(" + enName + ") as " + enName).
                collect(Collectors.joining(","));
        Storage storage = rule.getStorage();
        String tableName = storage.getTableName(rule.getEnName(), DimensionEnum.USER, PeriodEnum.DAY);
        String dimensionColumn = StandardSqlParam.getDimensionColumn(dimension);
        return "select "+ dimensionColumn + ", " + queryColumn +
                " from " + tableName +
                " where " + StandardSqlParam.COUNT_TIME + " = :" + StandardSqlParam.REQUEST_TIME +
                " and " + buildDimensionCodition(dimension);
    }

    private static String buildNotPeriodAndDimension(Rule rule, DimensionEnum dimension, PeriodEnum period) {
        List<Collect> collects = rule.getPredicateCollect(Metadata::notPeriodAndDimension);
        if (CollectionUtils.isEmpty(collects)) {
            return null;
        }
        //获取统计的列  a,b,c 。 sum(a) as a, sum(b) as b, sum(c) as c,
        String queryColumn = collects.stream().map(Collect::getEnName).distinct().
                map(enName -> "sum(" + enName + ") as " + enName).
                collect(Collectors.joining(","));
        Storage storage = rule.getStorage();
        String tableName = storage.getTableName(rule.getEnName(), DimensionEnum.USER, PeriodEnum.DAY);
        String dimensionColumn = StandardSqlParam.getDimensionColumn(dimension);
        return "select "+ dimensionColumn + ", " + queryColumn +
                " from " + tableName +
                " where " + StandardSqlParam.COUNT_TIME + " = :" + StandardSqlParam.REQUEST_TIME +
                " and " + buildDimensionCodition(dimension) +
                " group by "+ dimensionColumn;
    }

    private static String buildPeriodAndNotDimension(Rule rule, DimensionEnum dimension, PeriodEnum period) {
        List<Collect> collects = rule.getPredicateCollect(Metadata::periodAndNotDimension);
        if (CollectionUtils.isEmpty(collects)) {
            return null;
        }
        //获取统计的列  a,b,c 。 sum(a) as a, sum(b) as b, sum(c) as c,
        String queryColumn = collects.stream().map(Collect::getEnName).distinct().
                map(enName -> "sum(" + enName + ") as " + enName).
                collect(Collectors.joining(","));
        Storage storage = rule.getStorage();
        String tableName = storage.getTableName(rule.getEnName(), DimensionEnum.USER, PeriodEnum.DAY);
        String dimensionColumn = StandardSqlParam.getDimensionColumn(dimension);
        return "select "+ dimensionColumn + ", " + queryColumn +
                " from " + tableName +
                " where " + StandardSqlParam.COUNT_TIME + ">= :" + StandardSqlParam.START_TIME +
                " and " + StandardSqlParam.COUNT_TIME + "<= :" + StandardSqlParam.END_TIME +
                " and " + buildDimensionCodition(dimension);
    }

    private static String buildPeriodAndDimension(Rule rule, DimensionEnum dimension, PeriodEnum period) {
        List<Collect> collects = rule.getPredicateCollect(Metadata::periodAndDimension);
        if (CollectionUtils.isEmpty(collects)) {
            return null;
        }
        //获取统计的列  a,b,c 。 sum(a) as a, sum(b) as b, sum(c) as c,
        String queryColumn = collects.stream().map(Collect::getEnName).distinct().
                map(enName -> "sum(" + enName + ") as " + enName).
                collect(Collectors.joining(","));
        Storage storage = rule.getStorage();
        String tableName = storage.getTableName(rule.getEnName(), DimensionEnum.USER, PeriodEnum.DAY);
        String dimensionColumn = StandardSqlParam.getDimensionColumn(dimension);
        return "select "+ dimensionColumn + ", " + queryColumn +
                " from " + tableName +
                " where " + StandardSqlParam.COUNT_TIME + ">= :" + StandardSqlParam.START_TIME +
                " and " + StandardSqlParam.COUNT_TIME + "<= :" + StandardSqlParam.END_TIME +
                " and " + buildDimensionCodition(dimension) +
                " group by "+ dimensionColumn;
    }

    public static String buildDimensionCodition(DimensionEnum dimension) {
        return switch (dimension) {
            case USER -> StandardSqlParam.USER_ID + " in (:"+ StandardSqlParam.USER_IDS + ")";
            case GROUP -> StandardSqlParam.GROUP_ID + " in (:"+StandardSqlParam.GROUP_ID+ ")";
            case SCHOLL -> StandardSqlParam.SCHOOL_ID + " =:"+StandardSqlParam.SCHOOL_ID;
        };
    }
}
